/**
	********************************************************************************
	*
	*文件名称：
	*
	*文件日期：
	*
	*文件描述：
	*
	*文件版本：
	*
	*修改历史：
	*
	********************************************************************************
***/

/*******************************	头文件包含区	**********************************/
#include "device_time.h"

/******************************* 全局变量定义区 **********************************/

sun_struct sun = {180.0 * 3600 / M_PI};
const beijing_time baseDate = {0, 0, 0, 2, 1, 1, 19};
const uint8_t mon_table[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

/**
	*********************************************************************************
	*
	*函数名称：
	*
	*函数参数：无
	*
	*函数返回值：无
	*
	*功能描述：
	*
	*********************************************************************************
**/
static double timeToDouble(int y, int M, double d)
{
	double B = 0;
	double jd = 0;

	//设Y为给定年份，M为月份，D为该月日期（可以带小数）。
	//若M > 2，Y和M不变，若 M =1或2，以Y–1代Y，以M+12代M，换句话说，如果日期在1月或2月，则被看作是在前一年的13月或14月。
	//对格里高利历有 ：A = INT（Y/100）   B = 2 - A + INT(A/4)
	//对儒略历，取 B = 0
	//JD = INT(365.25(Y+4716))+INT(30.6001(M+1))+D+B-1524.5 （7.1）
	B = -13;
	jd = floor(365.25 * (y + 4716)) + floor(30.60001 * (M + 1)) + B + d - 1524.5;
	return jd;
}

/**
	*********************************************************************************
	*
	*函数名称：
	*
	*函数参数：无
	*
	*函数返回值：无
	*
	*功能描述：
	*
	*********************************************************************************
**/
static double sunHJ(double t)
{
	double j;
	t = t + (32.0 * (t + 1.8) * (t + 1.8) - 20) / 86400.0 / 36525.0;
	// 儒略世纪年数,力学时
	j = 48950621.66 + 6283319653.318 * t + 53 * t * t - 994 + 334166 * cos(4.669257 + 628.307585 * t) + 3489 * cos(4.6261 + 1256.61517 * t) + 2060.6 * cos(2.67823 + 628.307585 * t) * t;
	return (j / 10000000);
}
/**
	*********************************************************************************
	*
	*函数名称：
	*
	*函数参数：无
	*
	*函数返回值：无
	*
	*功能描述：
	*
	*********************************************************************************
**/
static double mod(double num1, double num2)
{
	num2 = fabs(num2);
	// 只是取决于Num1的符号
	return num1 >= 0 ? (num1 - (floor(num1 / num2)) * num2) : ((floor(fabs(num1) / num2)) * num2 - fabs(num1));
}
/**
	*********************************************************************************
	*
	*函数名称：
	*
	*函数参数：无
	*
	*函数返回值：无
	*
	*功能描述：
	*
	*********************************************************************************
**/
static double degree(double ag)
{
	ag = mod(ag, 2 * M_PI);
	if (ag <= -M_PI)
	{
		ag = ag + 2 * M_PI;
	}
	else if (ag > M_PI)
	{
		ag = ag - 2 * M_PI;
	}
	return ag;
}

/**
	*********************************************************************************
	*
	*函数名称：
	*
	*函数参数：无
	*
	*函数返回值：无
	*
	*功能描述：
	*
	*********************************************************************************
**/
static void doubleToStr(double time, uint8_t *h, uint8_t *m, uint8_t *s)
{
	double t;
	//int h,m,s;

	t = time + 0.5;
	t = (t - (int)t) * 24;
	*h = (int)t;
	t = (t - *h) * 60;
	*m = (int)t;
	t = (t - *m) * 60;
	*s = (int)t;
	//sprintf(str,"%02d:%02d:%02d",h,m,s);
}
/**
	*********************************************************************************
	*
	*函数名称：
	*
	*函数参数：无
	*
	*函数返回值：无
	*
	*功能描述：
	*
	*********************************************************************************
**/
double sunRiseTime(double date, double lo, double la, double tz)
{
	double t, j, sinJ, cosJ, gst, E, a, D, cosH0, cosH1, H0, H1, H;
	date = date - tz;
	// 太阳黄经以及它的正余弦值
	t = date / 36525;
	j = sunHJ(t);
	// 太阳黄经以及它的正余弦值
	sinJ = sin(j);
	cosJ = cos(j);
	// 其中2*M_PI*(0.7790572732640 + 1.00273781191135448*jd)恒星时（子午圈位置）
	gst = 2 * M_PI * (0.779057273264 + 1.00273781191135 * date) + (0.014506 + 4612.15739966 * t + 1.39667721 * t * t) / sun.RAD;
	E = (84381.406 - 46.836769 * t) / sun.RAD;									// 黄赤交角
	a = atan2(sinJ * cos(E), cosJ);												// '太阳赤经
	D = asin(sin(E) * sinJ);													// 太阳赤纬
	cosH0 = (sin(-50 * 60 / sun.RAD) - sin(la) * sin(D)) / (cos(la) * cos(D));	// 日出的时角计算，地平线下50分
	cosH1 = (sin(-6 * 3600 / sun.RAD) - sin(la) * sin(D)) / (cos(la) * cos(D)); // 天亮的时角计算，地平线下6度，若为航海请改为地平线下12度
	// 严格应当区分极昼极夜，本程序不算
	if (cosH0 >= 1 || cosH0 <= -1)
	{
		return -0.5; // 极昼
	}
	H0 = -acos(cosH0); // 升点时角（日出）若去掉负号 就是降点时角，也可以利用中天和升点计算
	H1 = -acos(cosH1);
	H = gst - lo - a;									  // 太阳时角
	sun.midDayTime = date - degree(H) / M_PI / 2 + tz;	  // 中天时间
	sun.dawnTime = date - degree(H - H1) / M_PI / 2 + tz; // 天亮时间
	return date - degree(H - H0) / M_PI / 2 + tz;		  // 日出时间，函数返回值
}
/**
	*********************************************************************************
	*
	*函数名称：
	*
	*函数参数：无
	*
	*函数返回值：无
	*
	*功能描述：
	*
	*********************************************************************************
**/

void get_sunset_rise_time(void)
{
	int i;
	static volatile char timestr[20];
	uint8_t hour;
	uint8_t min;
	int tz;
	double jd_degrees;
	double jd_seconds;
	double wd_degrees;
	double wd_seconds;

	if (reg.W.longitude_h >> 8 == 0)
	{
		jd_degrees = reg.W.longitude_h & 0x00FF;
	}
	else
	{
		jd_degrees = 360 - (reg.W.longitude_h & 0x00FF);
	}
	jd_seconds = reg.W.longitude_l >> 8;
	if (reg.W.dimensionality_h >> 8 == 0)
	{
		wd_degrees = reg.W.dimensionality_h & 0x00FF;
	}
	else
	{
		wd_degrees = -(reg.W.dimensionality_h & 0x00FF);
	}
	wd_seconds = reg.W.dimensionality_l >> 8;
	tz = 8;

	//step 1
	sun.longitude = -(jd_degrees + jd_seconds / 60) / 180 * M_PI;
	sun.dimensionality = (wd_degrees + wd_seconds / 60) / 180 * M_PI;
	//step 2
	sun.sunset = timeToDouble(rtc_time.year + 2000, rtc_time.month, rtc_time.date) - 2451544.5;
	for (i = 0; i < 10; i++)
	{
		sun.sunset = sunRiseTime(sun.sunset, sun.longitude, sun.dimensionality, tz / 24.0); // 逐步逼近法算10次
	}

	doubleToStr(sun.sunset, &hour, &min, &sun.sunset_s);
	reg.R.sunset = (hour << 8) + min;

	doubleToStr(sun.midDayTime + sun.midDayTime - sun.sunset, &hour, &min, &sun.sunrise_s);
	reg.R.sunrise = (hour << 8) + min;
}
/**
	*********************************************************************************
	*
	*函数名称：
	*
	*函数参数：无
	*
	*函数返回值：无
	*
	*功能描述：
	*
	*********************************************************************************
**/
beijing_time UTCToBeijing(uint16_t UTCyear, uint8_t UTCmonth, uint8_t UTCday, uint16_t UTChour, uint8_t UTCminute, uint8_t UTCsecond)
{
	beijing_time beijingTime = {0};
	int lastday = 0;	 // 月的最后一天日期
	int lastlastday = 0; //上月的最后一天日期

	lastlastday = lastlastday;

	beijingTime.year = UTCyear;
	beijingTime.month = UTCmonth;
	beijingTime.date = UTCday;
	beijingTime.hour = UTChour + 8 ; //UTC+8转换为北京时间
	beijingTime.min = UTCminute;
	beijingTime.sec = UTCsecond;

	if (beijingTime.month == 1 || beijingTime.month == 3 || beijingTime.month == 5 || beijingTime.month == 7 || beijingTime.month == 8 || beijingTime.month == 10 || beijingTime.month == 12)
	{
		lastday = 31;
		if (beijingTime.month == 3)
		{
			if ((beijingTime.year % 400 == 0) || (beijingTime.year % 4 == 0 && beijingTime.year % 100 != 0)) //判断是否为闰年
				lastlastday = 29;																			 //闰年的2月为29天，平年为28天
			else
				lastlastday = 28;
		}
		if (beijingTime.month == 8)
			lastlastday = 31;
	}
	else if (beijingTime.month == 4 || beijingTime.month == 6 || beijingTime.month == 9 || beijingTime.month == 11)
	{
		lastday = 30;
		lastlastday = 31;
	}
	else
	{
		lastlastday = 31;
		if ((beijingTime.year % 400 == 0) || (beijingTime.year % 4 == 0 && beijingTime.year % 100 != 0)) //闰年的2月为29天，平年为28天
			lastday = 29;
		else
			lastday = 28;
	}
	if (beijingTime.hour >= 24) //当算出的时大于或等于24：00时，应减去24：00，日期加一天
	{
		beijingTime.hour -= 24;
		beijingTime.date += 1;
		if (beijingTime.date > lastday) //当算出的日期大于该月最后一天时，应减去该月最后一天的日期，月份加上一个月
		{
			beijingTime.date -= lastday;
			beijingTime.month += 1;

			if (beijingTime.month > 12) //当算出的月份大于12，应减去12，年份加上1年
			{
				beijingTime.month -= 12;
				beijingTime.year += 1;
			}
		}
	}

	return beijingTime;
}

/**
	*********************************************************************************
	*
	*函数名称：
	*
	*函数参数：无
	*
	*函数返回值：无
	*
	*功能描述：
	*
	*********************************************************************************
**/
time calculatePowerOnTime(uint16_t UTCyear, uint8_t UTCmonth, uint8_t UTCday, uint16_t UTChour, uint8_t UTCminute, uint8_t UTCsecond,uint16_t Internet_time)
{
	time beijingTime = {0};
	int lastday = 0;	 // 月的最后一天日期
	int lastlastday = 0; //上月的最后一天日期

	lastlastday = lastlastday;

	beijingTime.year = UTCyear;
	beijingTime.month = UTCmonth;
	beijingTime.date = UTCday;
	beijingTime.hour = UTChour - (Internet_time / (60*60)); 
	beijingTime.min = UTCminute -  (Internet_time % (60 *60) / 60);
	beijingTime.sec = UTCsecond - (Internet_time % 60);

	if (beijingTime.month == 1 || beijingTime.month == 3 || beijingTime.month == 5 || beijingTime.month == 7 || beijingTime.month == 8 || beijingTime.month == 10 || beijingTime.month == 12)
	{
		lastday = 31;
		if (beijingTime.month == 3)
		{
			if ((beijingTime.year % 400 == 0) || (beijingTime.year % 4 == 0 && beijingTime.year % 100 != 0)) //判断是否为闰年
				lastlastday = 29;																			 //闰年的2月为29天，平年为28天
			else
				lastlastday = 28;
		}
		else if (beijingTime.month == 8)
			lastlastday = 31; //上个月天数
		else if (beijingTime.month == 1)
			lastlastday = 31; //上个月天数
		else
			lastlastday = 30; //上个月天数
	}
	else if (beijingTime.month == 4 || beijingTime.month == 6 || beijingTime.month == 9 || beijingTime.month == 11)
	{
		lastday = 30;
		lastlastday = 31;
	}
	else
	{
		lastlastday = 31;
		if ((beijingTime.year % 400 == 0) || (beijingTime.year % 4 == 0 && beijingTime.year % 100 != 0)) //闰年的2月为29天，平年为28天
			lastday = 29;
		else
			lastday = 28;
	}
	lastday = lastday;
	if(beijingTime.sec < 0)
	{
		beijingTime.sec = beijingTime.sec + 60;
		beijingTime.min --;
	}
	if(beijingTime.min < 0)
	{
		beijingTime.min = beijingTime.min + 60;
		beijingTime.hour --;
	}
	if(beijingTime.hour < 0)
	{
		beijingTime.hour = beijingTime.hour + 24;
		beijingTime.date --;
	}
	if(beijingTime.date <= 0)
	{
		beijingTime.date =beijingTime.date + lastlastday; 
		beijingTime.month -- ;
	}
	if(beijingTime.month <= 0)
	{
		beijingTime.month = beijingTime.month + 12;
		beijingTime.year --;
	}

	reg.R.powerOn_year_mon = (beijingTime.year << 8) + beijingTime.month;
	reg.R.powerOn_day_hour = (beijingTime.date << 8) + beijingTime.hour;
	reg.R.powerOn_min_sec = (beijingTime.min << 8) + beijingTime.sec;
	leakDete.time_on = 1;
	return beijingTime;
}
/**
	*********************************************************************************
	*
	*函数名称：
	*
	*函数参数：无
	*
	*函数返回值：无
	*
	*功能描述：
	*
	*********************************************************************************
**/
char CaculateWeekDay(int y, int m, int d)
{
	int week;
	if (m == 1 || m == 2)
	{
		m += 12;
		y--;
	}
	week = (d + 2 * m + 3 * (m + 1) / 5 + y + y / 4 - y / 100 + y / 400) % 7;
	return (week + 1);
}
/**
	*********************************************************************************
	*
	*函数名称：
	*
	*函数参数：无
	*
	*函数返回值：无
	*
	*功能描述：
	*
	*********************************************************************************
**/
void set_rtc_time(uint8_t y, uint8_t mon, uint8_t d, uint8_t h, uint8_t min, uint8_t s)
{
	rtc_time = UTCToBeijing(y, mon, d, h, min, s);									//utc转北京时间
	rtc_time.iWeek = CaculateWeekDay(rtc_time.year, rtc_time.month, rtc_time.date); //计算星期

	fnSysClock_Write(&rtc_time);
}

/**
	*********************************************************************************
	*
	*函数名称：
	*
	*函数参数：无
	*
	*函数返回值：无
	*
	*功能描述：
	*
	*********************************************************************************
**/
extern u8 led1RestartFlag; //刚上电时触发渐变开关灯效果标志
extern uint16_t time_count;

void get_rtc_time(void)
{
	static u8 flag = 0, i = 0;
	fnSysClock_Read(&rtc_time);

	if (rtc_time.year < 21 || rtc_time.year > 50 || led1RestartFlag != 2)
		return;

	reg.RW.year_mon = (rtc_time.year << 8) + rtc_time.month;
	reg.RW.day_hour = (rtc_time.date << 8) + rtc_time.hour;
	reg.RW.min_sec = (rtc_time.min << 8) + rtc_time.sec;

	if ((rtc_time.hour == 0 && rtc_time.min == 0 && rtc_time.sec == 0) || flag == 0)
	{
		if(flag == 0) 
		{
			u16 run_time = (reg.R.module_run_time >> 16 | reg.R.module_run_time << 16) * 60 + time_count/100;
			calculatePowerOnTime(rtc_time.year, rtc_time.month, rtc_time.date, rtc_time.hour, rtc_time.min, rtc_time.sec,run_time);
		}

		sort(timestampTable, reg.W.scene, SCENE_TIME_NUM); //根据时间戳对时间戳和策略时间表重新排序
		// //根据上个时间策略内容执行开关灯
		//去掉凌晨重新执行策略功能
		
		for (i = 0; i < compareStruct.lastScene.time_ctrl_ch_num; i++)
		{
			if((timestampTable[compareStruct.lastSceneSeek] == TimeDate_To_TimeStamp(rtc_time.year, rtc_time.month, rtc_time.date, 0, 0, 0)) || (flag == 0))
			{
				reg.RW.lamp[i] = compareStruct.lastScene.ctrl_buf[i];
			#if (PROJECT_TYPEDEF == NB_ONENET || PROJECT_TYPEDEF == NB)
				device_time.lamp_data_report_time = reg.W.heartbeat_interval * 100 + device_info.random - 100;
			#else
				device_time.lamp_data_report_time = 60000 + device_info.random - 100;
			#endif
			}	
		} 
		if(flag == 0)
		{
			leakDete.system_run_timestamp = TimeDate_To_TimeStamp(reg.R.powerOn_year_mon / 256, reg.R.powerOn_year_mon % 256, reg.R.powerOn_day_hour / 256, reg.R.powerOn_day_hour % 256, reg.R.powerOn_min_sec / 256, reg.R.powerOn_min_sec % 256);
//			check_lamp_sw(reg.RW.lamp[0].sw,reg.RW.lamp[1].sw, leakDete.system_run_timestamp);
//			check_lamp_sw(reg.RW.lamp[0].sw,reg.RW.lamp[1].sw, leakDete.system_run_timestamp);
			if(reg.RW.lamp[0].sw == 1 && reg.W.lamp1_power_on_timestamp  && leakDete.currut_lamp1_on_timestamp == 0)
			{
				leakDete.currut_lamp1_on_timestamp = leakDete.system_run_timestamp;
			}
			if(reg.RW.lamp[1].sw == 1 && reg.W.lamp2_power_on_timestamp  && leakDete.currut_lamp2_on_timestamp == 0)
			{
				leakDete.currut_lamp2_on_timestamp = leakDete.system_run_timestamp;
			}
			LOGD("power_on time:%u\r\n", leakDete.system_run_timestamp);
			LOGD("\r\n year:%u, montn:%u,date:%u,hour:%u,min:%u,sec:%u \r\n",rtc_time.year,rtc_time.month, rtc_time.date,rtc_time.hour,rtc_time.min,rtc_time.sec);
			LOGD("\r\nON year:%u, montn:%u,date:%u,hour:%u,min:%u,sec:%u \r\n",reg.R.powerOn_year_mon / 256, reg.R.powerOn_year_mon % 256, reg.R.powerOn_day_hour / 256, reg.R.powerOn_day_hour % 256, reg.R.powerOn_min_sec / 256, reg.R.powerOn_min_sec % 256);
		}
		flag = 1;
	}
	else
	{
		/*do nothing*/
	}
}

/**
	*********************************************************************************
	*
	*函数名称：
	*
	*函数参数：无
	*
	*函数返回值：无
	*
	*功能描述：
	*
	*********************************************************************************
**/
char Is_Leap_Year(uint8_t year)
{
	year = year + 2000;
	if (year % 4 == 0)
		return 1; //必须能被4整除
	else
		return 0;
}

/**
	*********************************************************************************
	*
	*函数名称：
	*
	*函数参数：无
	*
	*函数返回值：无
	*
	*功能描述：
	*
	*********************************************************************************
**/
uint32_t TimeDate_To_TimeStamp(uint16_t year, uint8_t mon, uint8_t date, uint8_t hour, uint8_t min, uint8_t sec)
{
	uint8_t t;
	volatile uint64_t stimstamp = 1546272000; //2019.1.1 0点的时间戳
	if (year < baseDate.year || year > 99 || mon > 12)
	{
		return 0;
	}
	for (t = baseDate.year; t < year; t++) //把所有年份的秒钟相加
	{
		if (Is_Leap_Year(t))
		{
			stimstamp += 31622400; //闰年的秒钟数
		}
		else
		{
			stimstamp += 31536000; //平年的秒钟数
		}
	}
	mon -= 1;
	for (t = 0; t < mon; t++) //把前面月份的秒钟数相加
	{
		stimstamp += (uint32_t)mon_table[t] * 86400; //月份秒钟数相加
		if (t == 1 && Is_Leap_Year(year))
		{
			stimstamp += 86400; //闰年2月份增加一天的秒钟数
		}
	}
	stimstamp += (uint32_t)(date - 1) * 86400; //把本月前面日期的秒钟数相加
	stimstamp += (uint32_t)hour * 3600;		   //小时秒钟数
	stimstamp += (uint32_t)min * 60;		   //分钟秒钟数
	stimstamp += sec;						   //最后的秒钟加上去
	return stimstamp;
}
